using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Linq;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
using DBSourceToolsLib;
using DBConnectionLib;
using DBSourceToolsLib.SysObjects;
using Fireball.Windows.Forms;
using Fireball.Syntax;
using Fireball.CodeEditor.SyntaxFiles;
using ICSharpCode.TextEditor.Document;

namespace DBSourceTools
{
    public partial class TextEditorForm : MainPanelForm
    {
        private ScriptRunner m_ScriptRunner;
        delegate void SetTextCallback(string text);
        private string m_ShortName;
        private QueryType m_QueryType;
        private string m_Params;
        private bool m_bIsRunning = false;
        private bool m_CTRL_K = false;
        private bool m_bSaved = true;

        ICSharpCode.MSSQLExtensions.MSSQLTextEditor m_MsSqlTextEditor;
        //CodeEditorControl m_CodeEdit = new CodeEditorControl();

        // TODO : Save on close

        public TextEditorForm(string strDBName, string strObjType, string strName, DBSourceToolsLib.SysObjects.QueryType oQueryType,
            string strParams)
            : base(strDBName, strObjType, strName)
        {
            InitializeComponent();
            m_ShortName = strName.Substring(strName.LastIndexOf("\\") +1);
            m_ShortName = m_ShortName.Substring(0, m_ShortName.LastIndexOf("."));

            m_QueryType = oQueryType;
            m_Params = strParams;

        }

        private void TextEditorForm_Load(object sender, EventArgs e)
        {
            m_MsSqlTextEditor = new ICSharpCode.MSSQLExtensions.MSSQLTextEditor(this);
            this.Text = m_Name;
            this.TabText = m_Name;
            SourceDatabase oDatabase = UserSettings.Instance().getSourceDatabase(m_DatabaseName);
            
            // TODO : Patches intellisense - get reference to source database
            if ((oDatabase != null) && (!UserSettings.Instance().DisableIntellisense))
            {
                // we could just be opening a file.
                this.toolStripLabelCurrentDatabase.Text = oDatabase.DataSourceName + " : " + oDatabase.DatabaseName;

                m_MsSqlTextEditor.SetComposite(oDatabase.GetDBComposite());

            }

            // text comes from get panel.
            this.splitContainer1.Panel1.Controls.Add(this.GetPanel());
            m_MsSqlTextEditor.Document.DocumentChanged += new ICSharpCode.TextEditor.Document.DocumentEventHandler(Document_DocumentChanged);

//            this.m_CodeEdit.TextChanged += new EventHandler(m_CodeEdit_TextChanged);

            this.splitContainer1.Panel2Collapsed = true;
            this.btnSave.Enabled = false;
            //this.m_CodeEdit.Saved = true;

            if ((! String.IsNullOrEmpty(m_Params)) && (m_Params.CompareTo("Generated") == 0)) {
                SysObject oSysObject = oDatabase.SysObjects.getSysObject("Queries", m_ShortName);
                m_MsSqlTextEditor.Document.TextBufferStrategy.SetContent(oSysObject.Text);
                //m_CodeEdit.Document.Text = oSysObject.Text;

            }

            if (!String.IsNullOrEmpty(m_Params))
            {
                short iLineNo;
                if (Int16.TryParse(m_Params, out iLineNo))
                {
                    GotoLine(iLineNo);
                }

            }

            m_toolStripButton_Cancel.Visible = false;
            m_ScriptRunner = new ScriptRunner(oDatabase, true);
            m_ScriptRunner.ProgressEventPublisher += new EventHandler<ProgressEventArgs>(oScriptRunner_ProgressEventPublisher);
            
        }

        void Document_DocumentChanged(object sender, ICSharpCode.TextEditor.Document.DocumentEventArgs e)
        {
            // flag save button.
            m_bSaved = false;
            btnSave.Enabled = (!m_bSaved);
            //throw new NotImplementedException();
        }

        //void m_CodeEdit_KeyUp(object sender, KeyEventArgs e)
        //{
        //    if ((e.Control) && e.KeyCode == Keys.K)
        //    {
        //        m_CTRL_K = true;
        //    }
        //    else
        //        m_CTRL_K = false;
        //}

        void m_CodeEdit_TextChanged(object sender, EventArgs e)
        {
            this.btnSave.Enabled = true;
        }

        private System.Windows.Forms.Panel GetPanel()
        {
            Panel oPanel = new Panel();
            oPanel.Dock = DockStyle.Fill;

            m_MsSqlTextEditor.Dock = DockStyle.Fill;

            //m_CodeEdit.Dock = DockStyle.Fill;
            //CodeEditorSyntaxLoader.SetSyntax(m_CodeEdit, this.m_Name);

            if (System.IO.File.Exists(this.m_Name))
            {
                m_MsSqlTextEditor.Document.HighlightingStrategy =
                    ICSharpCode.TextEditor.Document.HighlightingStrategyFactory.CreateHighlightingStrategyForFile(m_Name);

                using (System.IO.StreamReader oReader = new System.IO.StreamReader(this.m_Name))
                {
                    m_MsSqlTextEditor.Text = oReader.ReadToEnd();
                    //m_MsSqlTextEditor.Document.TextBufferStrategy.SetContent(oReader.ReadToEnd());
                    //m_CodeEdit.Document.Text = oReader.ReadToEnd();
                }
            }
            //oPanel.Controls.Add(m_CodeEdit);
            oPanel.Controls.Add(m_MsSqlTextEditor);
            m_MsSqlTextEditor.FlagFoldingUpdate();
            return oPanel;

        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFile(base.m_Name);
            
        }

        private void btnSaveAs_Click(object sender, EventArgs e)
        {
            if (this.saveFileDialog1.ShowDialog(this) == DialogResult.OK)
            {
                SaveFile(this.saveFileDialog1.FileName);
            }
        }

        private void SaveFile(string strFileName)
        {
            try
            {
                //ensure directory exists.
                DBSourceToolsLib.FileUtils oUtils = new FileUtils();
                oUtils.EnsureFileDirectoryExists(strFileName);
                FileUtils.EnsureFileIsWriteable(strFileName);
                using (System.IO.StreamWriter oWriter = new System.IO.StreamWriter(strFileName))
                {
                    oWriter.Write(m_MsSqlTextEditor.Document.TextContent);
                    //oWriter.Write(m_CodeEdit.Document.Text);
                }
                this.btnSave.Enabled = false;
                m_bSaved = true;
                
            }
            catch (Exception ex)
            {
                DBSourceToolsLib.SysObjects.UI.Forms.ErrorForm oError =
                    new DBSourceToolsLib.SysObjects.UI.Forms.ErrorForm(ex.ToString());
                oError.Show();

//                MessageBox.Show(ex.ToString());
            }

        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            //if (!this.m_CodeEdit.Saved)
            if (!m_bSaved)
            {
                if (MessageBox.Show("Save changes to " + base.m_Name, "Save", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    SaveFile(base.m_Name);
                }
            }
            this.Close();

        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            SaveFile(base.m_Name);
        }

        private void runScriptToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.splitContainer1.Panel2Collapsed = false;

            
            //string strExecuteText = m_CodeEdit.Document.Text;
            string strExecuteText = m_MsSqlTextEditor.Document.TextContent;

            if (m_MsSqlTextEditor.ActiveTextAreaControl.SelectionManager.HasSomethingSelected)
                strExecuteText = m_MsSqlTextEditor.ActiveTextAreaControl.SelectionManager.SelectedText;
            //if (m_CodeEdit.Selection.Text.Length > 0)
            //    strExecuteText = m_CodeEdit.Selection.Text;

            // check if the execute text starts with select - if so, output to datagrid.
            DBSourceToolsLib.CodeParser.CodeParser oCodeParser = new DBSourceToolsLib.CodeParser.CodeParser();
            // clear messages
            m_textBoxOutput.Clear();

            if (oCodeParser.StartsWithSelect(strExecuteText))
            {
                RunSelect(strExecuteText);
            }
            else
            {
                // if not, run using script file.
                RunScript(this.m_DatabaseName, this.m_ObjectType, this.m_ShortName, strExecuteText, true);
            }

        }

        private void RunSelect(string strExecuteText)
        {
            this.Cursor = Cursors.WaitCursor;
            try
            {
                this.m_dataGridViewResults.DataSource = GetSelectData(strExecuteText);
                this.m_dataGridViewResults.DataMember = "mydata";
                this.m_tabControlResults.SelectedTab = m_tabPageResultsGrid;
            }
            catch (System.Data.SqlClient.SqlException sqlEx)
            {
                string strErrText = string.Format("Msg {0}, Level {1}, State {2}, Server {3}, Line {4}",
                    sqlEx.Number, sqlEx.Class, sqlEx.State, sqlEx.Server, sqlEx.LineNumber);
                strErrText += Environment.NewLine + sqlEx.Message;
                SetText(strErrText);
                this.m_tabControlResults.SelectedTab = m_tabPageTextResults;
            }
            catch (Exception ex)
            {
                SetText(ex.ToString());
                this.m_tabControlResults.SelectedTab = m_tabPageTextResults;
            }
            this.Cursor = Cursors.Default;
        }

        private object GetSelectData(string strExecuteText)
        {
            
            DataSet oDS = new DataSet();

            SourceDatabase oDatabase = UserSettings.Instance().getSourceDatabase(m_DatabaseName);
            using (System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(oDatabase.ConnectionString))
            {
                conn.Open();
                System.Data.SqlClient.SqlDataAdapter oDA = new System.Data.SqlClient.SqlDataAdapter();
                oDA.SelectCommand = new System.Data.SqlClient.SqlCommand(strExecuteText, conn);
                oDA.Fill(oDS, "mydata");
                conn.Close();
            }
            
            return oDS;

        }

        public void RunScript(string strDatabase, string strObjectType, string strName, string strSelectedText, bool useFileMode)
        {
            // TODO : COMPLETE 1.2.0.8 make messages output copyable. (billhuber)

            m_bIsRunning = true;
            m_toolStripButton_Cancel.Visible = true;
            this.m_textBoxOutput.Clear();

//            if (useFileMode)
              //  m_ScriptRunner.RunScriptUsingFile(this.m_Name, strSelectedText);
            //else
                m_ScriptRunner.RunScriptUsingStreams(strName, strSelectedText);
                this.m_tabControlResults.SelectedTab = m_tabPageTextResults;
        }


        private void hideOutputToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.splitContainer1.Panel2Collapsed = (!this.splitContainer1.Panel2Collapsed);
        }

        void oScriptRunner_ProgressEventPublisher(object sender, ProgressEventArgs e)
        {

            if (e.Percent.CompareTo("100") == 0)
            {
                this.SetText(this.m_ScriptRunner.GetResultsText());
                //this.m_toolStripStatusLabelOutput.Text = "Done";
                m_toolStripButton_Cancel.Visible = false;
                m_bIsRunning = false;
            }
            this.SetText(e.Message + Environment.NewLine);
        }


        private void SetText(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.m_textBoxOutput.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.m_textBoxOutput.AppendText(text);
                this.m_textBoxOutput.Update();
            }
        }


        internal void GotoLine(int iLine)
        {
            // TODO MSSqlTextEditor goto line
            m_MsSqlTextEditor.ActiveTextAreaControl.ScrollTo(iLine);
            //m_CodeEdit.GotoLine(iLine -1);
            //m_CodeEdit.ScrollIntoView(iLine - 1);
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            runScriptToolStripMenuItem_Click(sender, e);
        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            // Cancel Script
            if (this.m_bIsRunning)
            {
                SetText("Aborting ...");
                this.m_ScriptRunner.Abort();
                // do not call a join here -- dont understand why behaviour is different from GUI and nUnit
                //this.m_ScriptRunner.Join();
                SetText("Aborted");
            }

        }

        private void toolStripButton2_Click_1(object sender, EventArgs e)
        {
            hideOutputToolStripMenuItem_Click(sender, e);
        }

        private void commentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_MsSqlTextEditor.ActiveTextAreaControl.TextArea.SelectionManager.HasSomethingSelected)
            {
                foreach (ISelection oISelection in m_MsSqlTextEditor.ActiveTextAreaControl.TextArea.SelectionManager.SelectionCollection)
                {
                    for (int iLineNo = oISelection.StartPosition.Line; iLineNo <= oISelection.EndPosition.Line; iLineNo++)
                    {
                        LineSegment oLineSegment = m_MsSqlTextEditor.Document.GetLineSegment(iLineNo);
                        m_MsSqlTextEditor.Document.Insert(oLineSegment.Offset, "--");
                    }
                }
            }
            else
            {
                LineSegment oLineSegment = m_MsSqlTextEditor.Document.GetLineSegmentForOffset(m_MsSqlTextEditor.ActiveTextAreaControl.Caret.Offset);
                m_MsSqlTextEditor.Document.Insert(oLineSegment.Offset, "--");
            }
        }

        private void unCommentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            List<int> lstRemoveAtOffset = new List<int>();
            if (m_MsSqlTextEditor.ActiveTextAreaControl.TextArea.SelectionManager.HasSomethingSelected)
            {
                foreach (ISelection oISelection in m_MsSqlTextEditor.ActiveTextAreaControl.TextArea.SelectionManager.SelectionCollection)
                {
                    for (int iLineNo = oISelection.StartPosition.Line; iLineNo <= oISelection.EndPosition.Line; iLineNo++)
                    {
                        LineSegment oLineSegment = m_MsSqlTextEditor.Document.GetLineSegment(iLineNo);

                        foreach (TextWord oWord in oLineSegment.Words)
                        {
                            string strWord = oWord.Word;
                            if (strWord.Equals("--"))
                            {
                                lstRemoveAtOffset.Add(oLineSegment.Offset + oWord.Offset);
                                break;
                            }
                        }
                    }
                }
            }
            else
            {
                LineSegment oLineSegment = m_MsSqlTextEditor.Document.GetLineSegmentForOffset(m_MsSqlTextEditor.ActiveTextAreaControl.Caret.Offset);
                foreach (TextWord oWord in oLineSegment.Words)
                {
                    string strWord = oWord.Word;
                    if (strWord.Equals("--"))
                    {
                        lstRemoveAtOffset.Add(oLineSegment.Offset + oWord.Offset);
                        break;
                    }
                }
            }

            foreach (int lOffset in lstRemoveAtOffset.OrderByDescending(i => i))
            {
                m_MsSqlTextEditor.Document.Remove(lOffset, 2);
            }

        }

        private void commentSelectionCtrlKCToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //m_CTRL_K = true;
            commentToolStripMenuItem_Click(sender, e);
            //m_CTRL_K = false;
        }

        private void uncommentToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            //m_CTRL_K = true;
            unCommentToolStripMenuItem_Click(sender, e);
            //m_CTRL_K = false;

        }


        //private void intellisenseCtrlSpaceToolStripMenuItem_Click(object sender, EventArgs e)
        //{
        //    int iMainHeight = 24 + 25;
        //    int iDocHeight = 25 + 24;
        //    int iFloatHeight = 25 + 21;
        //    Point oLocation = Point.Empty;
        //    if (this.IsFloat)
        //    {
        //        oLocation = this.FloatPane.FloatWindow.DesktopLocation;
        //        oLocation.Y += iFloatHeight;
        //    }
        //    else
        //    {
        //        oLocation = this.DesktopLocation;
        //        Point oDockLoc = this.ParentForm.DesktopLocation;
        //        oLocation.X += oDockLoc.X;
        //        oLocation.Y += oDockLoc.Y + iMainHeight + iDocHeight;
        //    }
            
        //    TextPoint oRange = m_CodeEdit.Caret.LogicalPosition;
        //    TextPoint oPosition = m_CodeEdit.Caret.Position;
        //    int iFirstVis = m_CodeEdit.ActiveViewControl.View.FirstVisibleRow;
        //    Row oRow = m_CodeEdit.Document[ oRange.Y];
            
        //    Size oSizeRow = m_CodeEdit.ActiveViewControl.Painter.MeasureRow(oRow, oRange.X); 
        //    oLocation.X += oSizeRow.Width + 5;
        //    oLocation.X += m_CodeEdit.ActiveViewControl.View.TextMargin;
        //    oLocation.Y += ((oRange.Y - iFirstVis + 1) * 16);
            
        //    IntellisenseTablesForm oForm = new IntellisenseTablesForm();
        //    oForm.StartPosition = FormStartPosition.Manual;
        //    oForm.DesktopLocation = oLocation;

        //    bool bIsTable = false;

        //    string strPrevWord = oRow.FindPrevWord(oRange.X, true, true);
        //    // find prevprevword.
        //    int iPrevWordIdx = oRange.X - strPrevWord.Length - 2 ;
        //    string strPrevPrevWord = oRow.FindPrevWord( (iPrevWordIdx < 1 ? 1 : iPrevWordIdx) , true, true);
        //    strPrevWord = strPrevWord.Replace("\t", "");
        //    strPrevPrevWord = strPrevPrevWord.Replace("\t", "");
        //    if ((strPrevWord.CompareTo(" ") == 0) || (strPrevWord.ToLower().CompareTo("dbo") == 0))
        //    {
        //        bIsTable = true;
        //        strPrevWord = null;
        //    }
        //    if (!string.IsNullOrEmpty(strPrevWord))
        //    {
        //        // return table list
        //        bIsTable = true;
        //        oForm.SetTableName(strPrevWord);
        //        oForm.SetTablePrefix(strPrevPrevWord);
        //    }
        //    else
        //    {
        //        // return rowlist.
        //    }

        //    oForm.ShowDialog(this);
        //    m_CodeEdit.Selection.Text += oForm.SelectedText;
        //    // move caret to the end of the new text.
        //    for (int i = 0; i < oForm.SelectedText.Length; i++)
        //        m_CodeEdit.Caret.MoveRight(false);
        //    //m_CodeEdit.Caret.SetPos(new TextPoint(oRange.X + oForm.SelectedText.Length, oRange.Y));
            
        //    m_CodeEdit.Selection.ClearSelection();
            
        //}

        //private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        //{
        //    Clipboard.SetText(m_CodeEdit.Selection.Text);
        //}

        //private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
        //{
            
        //    m_CodeEdit.Selection.Text = Clipboard.GetText();
        //}

        private void m_dataGridViewResults_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            // occurs on a data error i.e. displaying encrypted passwords
        }

        private void commentToolStripMenuItem_Click_1(object sender, EventArgs e)
        {
            commentToolStripMenuItem_Click(sender, e);
        }

        private void uncommentToolStripMenuItem_Click_1(object sender, EventArgs e)
        {
            unCommentToolStripMenuItem_Click(sender, e);
        }

    }
}

